/**
 *
 * Copyright (c) 2011, Anusorn B. All rights reserved.
 *
 * This is proprietary source code of Anusorn B.
 *
 */

/**
 * 
 */
package com.nk.system.dao.hibernate;

/**
 * @author Anusorn
 *
 */

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.nk.system.common.search.ExampleOptions;
import com.nk.system.common.search.Filter;
import com.nk.system.common.search.ISearch;
import com.nk.system.common.search.Search;
import com.nk.system.common.search.SearchResult;
import com.nk.system.dao.GenericDAO;

public class GenericDAOHibernate<T, ID extends Serializable> extends GenericBaseDAOHibernate implements GenericDAO<T, ID> {

	private Class<T> persistentClass;// = (Class<T>) DAOUtil.getTypeArguments(GenericDAOHibernate.class,
										// this.getClass()).get(0);

	@SuppressWarnings("unchecked")
	public GenericDAOHibernate() {
		super();
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

	}

	public Class<T> getPersistentClass() {

		return persistentClass;
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = false)
	public T findById(ID id, boolean lock) {
		T entity;
		if (lock) {
			entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
		} else {
			entity = (T) getSession().load(getPersistentClass(), id);
		}

		return entity;
	}

	@Transactional(readOnly = true)
	public List<T> findAll() {
		return findByCriteria();
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<T> findByExample(T exampleInstance) {

		Criteria crit = getSession().createCriteria(getPersistentClass());
		Example example = Example.create(exampleInstance);

		crit.add(example);

		return crit.list();
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<T> findByExample(T exampleInstance, int pageNumber, int pageSize) {

		Criteria crit = getSession().createCriteria(getPersistentClass());
		Example example = Example.create(exampleInstance);
		crit.add(example);
		crit.setFirstResult((pageNumber - 1) * pageSize);
		crit.setMaxResults(pageSize);
		return crit.list();
	}

	@Transactional(readOnly = false)
	public T saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);

		return entity;
	}

	@Transactional(readOnly = false)
	public T update(T entity) {
		getHibernateTemplate().update(entity);

		return entity;
	}

	@Transactional(readOnly = false)
	public T save(T entity) {
		getHibernateTemplate().save(entity);
		return entity;
	}

	@Transactional(readOnly = false)
	public void delete(T entity) {
		getHibernateTemplate().delete(entity);

	}

	public void flush() {
		getHibernateTemplate().flush();
	}

	public void clear() {
		getHibernateTemplate().clear();
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<T> findByExample(T exampleInstance, String[] excludeProperty) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		Example example = Example.create(exampleInstance);
		for (String exclude : excludeProperty) {
			example.excludeProperty(exclude);
		}
		crit.add(example);
		return crit.list();
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<T> findByExample(T exampleInstance, String[] excludeProperty, int pageNumber, int pageSize) {

		Criteria crit = getSession().createCriteria(getPersistentClass());
		Example example = Example.create(exampleInstance);
		for (String exclude : excludeProperty) {
			example.excludeProperty(exclude);
		}
		crit.add(example);
		crit.setFirstResult((pageNumber - 1) * pageSize);
		crit.setMaxResults(pageSize);
		return crit.list();
	}

	/**
	 * Use this inside subclasses as a convenience method.
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	private List<T> findByCriteria(Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		for (Criterion c : criterion) {
			crit.add(c);
		}
		return crit.list();
	}

	/* implement */
	@Transactional(readOnly = true)
	public int count(ISearch search) {
		if (search == null) {
			search = new Search();
		}
		return _count(persistentClass, search);
	}

	@Transactional(readOnly = true)
	public T[] find(ID... ids) {
		return _find(persistentClass, ids);
	}

	@Transactional(readOnly = true)
	public T getReference(ID id) {
		return _getReference(persistentClass, id);
	}

	@Transactional(readOnly = true)
	public T[] getReferences(ID... ids) {
		return _getReferences(persistentClass, ids);
	}

	public boolean isAttached(T entity) {
		return _contains(entity);
	}

	public void refresh(T... entities) {
		_refresh(entities);
	}

	@Transactional(readOnly = false)
	public boolean deleteById(ID id) {
		return _removeById(persistentClass, id);
	}

	@Transactional(readOnly = false)
	public void deleteByIds(ID... ids) {
		_removeByIds(persistentClass, ids);
	}

	public T merge(T entity) {
		return _merge(entity);
	}

	public T[] merge(T... entities) {
		return _merge(persistentClass, entities);
	}

	public void persist(T... entities) {
		_persist(entities);
	}

	@Transactional(readOnly = false)
	public T[] save(T... entities) {
		return _persistOrMerge(persistentClass, entities);
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public <RT> List<RT> search(ISearch search) {
		if (search == null) {
			return (List<RT>) findAll();
		}
		return _search(persistentClass, search);
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public <RT> SearchResult<RT> searchAndCount(ISearch search) {
		if (search == null) {
			SearchResult<RT> result = new SearchResult<RT>();
			result.setResult((List<RT>) findAll());
			result.setTotalCount(result.getResult().size());
			return result;
		}
		return _searchAndCount(persistentClass, search);
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public <RT> RT searchUnique(ISearch search) {
		return (RT) _searchUnique(persistentClass, search);
	}

	@Transactional(readOnly = true)
	public Filter getFilterFromExample(T example) {
		return _getFilterFromExample(example);
	}

	@Transactional(readOnly = true)
	public Filter getFilterFromExample(T example, ExampleOptions options) {
		return _getFilterFromExample(example, options);
	}

	@Autowired(required = true)
	public void setSession(org.hibernate.SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);

	}

}
